import java.io.*;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Scanner;


public class Scheduler {
	private ArrayList<String> mySchedule;
	private FileInputStream fstream;
	private DataInputStream in;
	private BufferedReader br;
	private FileWriter fWriter;
	private PrintWriter out;
	private String inFile;
	private String myScheduleFile;
	private String outFile;
	private InetAddress userInfo;
	private ArrayList<String> readyMemberList;
	private int readyMemberNum;
	private static boolean busy;
	private SchedulerGUI myGUI;
	private ArrayList<InetAddress> neighborList;
	private Scanner input;
	/**
	 * constructor of the class
	 */
	public Scheduler(){
		busy=false;
		mySchedule = new ArrayList<String>();
		neighborList = new ArrayList<InetAddress>();
		inFile = Constants.INFILENAME;
		myScheduleFile = Constants.MYSCHEDULE;
		outFile =Constants.OUTFILENAME;
		getSelfSchedule();
		input = new Scanner(System.in);
		Boolean ongoing=true;
		String member="";
		try {

			userInfo  = InetAddress.getLocalHost();
			System.out.printf("Enter the host name of the computer you want to connect to: ");
			member=input.nextLine();
			member=member.replaceAll("\n", "");
			neighborList.add(InetAddress.getByName(member));
			System.out.printf("more?(enter 0 to end adding member): ");
			member=input.nextLine();
			member=member.replaceAll("\n", "");
			if(member.compareTo("0")==0)
				ongoing=false;
			while(ongoing){
				neighborList.add(InetAddress.getByName(member));
				System.out.printf("more?(enter 0 to end adding member): ");
				member=input.nextLine();
				member=member.replaceAll("\n", "");
				if(member.compareTo("0")==0)
					ongoing=false;
			}
			/*neighborList.add(InetAddress.getByName("Indira-PC"));
			System.out.println(neighborList.get(0).getHostAddress());
			neighborList.add(InetAddress.getByName("Saiki-HP"));
			System.out.println(neighborList.get(1).getHostAddress());*/
		} catch (UnknownHostException e) {
			e.printStackTrace();
		}
		myGUI = new SchedulerGUI(this);
	}

	public ArrayList<InetAddress>getNeighborList (){
		return neighborList;
	}

	public SchedulerGUI getGUI(){
		return myGUI;
	}

	public String getIP(){
		return userInfo.getHostAddress();
	}

	/**
	 * get the lock status of this scheduler
	 * @return status of the lock
	 */
	public boolean getLockStatus (){
		return busy;
	}

	/**
	 * set the status of the lock
	 * @param newStatus new status of the lock
	 */
	public void setLockStatus(Boolean newStatus){
		busy = newStatus;
	}

	/**
	 * create a meeting scheduling
	 * @param memberList list of the member
	 */
	public void createMeeting(String memberList){
		Scanner input = new Scanner(System.in);
		readyMemberNum=0;	//reset readyMemberNum
		String[] memberArray = null;
		readyMemberList= new ArrayList<String>();
		memberArray= memberList.split(",",2);
		Client client = new Client();
		int memberSize=0;
		if(!busy){	// if not waiting for another plan
			busy=true; //set lock
			// create member list
			// pre-scheduling notification
			memberSize = preSchedulingNote(memberList);
			myGUI.updateSystemMessage("waiting for members' responses");
			while(readyMemberNum<memberSize	// not all members reply ready yet
					&&readyMemberNum>=0)	// none member reply busy
				Thread.yield(); //wait
			System.out.println("press enter to continue");
			input.nextLine();
			if(readyMemberNum>=0)
			{	// all return ready
				// create new output file
				if(memberList.contains(","))	// has more than one member
					writeNewOutputFile(userInfo.getHostAddress(),memberArray[1],mySchedule);
				else	// only one member
					writeNewOutputFile(userInfo.getHostAddress(),"",mySchedule);
				// send to next member
				try {
					client.sendFile(memberArray[0],outFile);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
			else{	// one not ready message return
				myGUI.updateSystemMessage("one or more members is not ready for scheduling");
				busy=false;	//reset lock
				schedulingCancel();
			}
		}
		else // IS waiting for another plan
			myGUI.updateSystemMessage("Can not make new plan untill current one is finished");
	}

	/**
	 * send pre-scheduling not to all members to confirm availability
	 * @param memberList the remaining members
	 * @return size of the member list
	 */
	private int preSchedulingNote(String memberList) {
		Client client = new Client();
		String[] memberArray=memberList.split(",");
		System.out.println("Tell all members there will be an scheduling for meeting, check if all members are ready");
		for(int i=0;i<memberArray.length;i++)
			try {
				client.availabilityCheck(memberArray[i], userInfo.getHostAddress());
			} catch (Exception e) {
				e.printStackTrace();
			}
		return memberArray.length;
	}

	/**
	 * schedule is canceled due to either no common schedule or one member is not ready
	 */
	public void schedulingCancel(){
		Client client = new Client();
		System.out.println("tell all member scheduling cancel");
		for(int i=0;i<readyMemberList.size();i++)
			try {
				client.cancelScheduling(readyMemberList.get(i));
			} catch (Exception e) {
				e.printStackTrace();
			}
	}

	/**
	 * add a member to the list of ready members
	 * @param member member who is ready for scheduling
	 */
	public void addReadyMember(String member){
		readyMemberList.add(member);
	}

	/**
	 * response from a member about his/her lock status, and user acts accordingly
	 * @param status busy or not
	 */
	public void memberLockStatus(Boolean status){
		if(!status)// not busy
			readyMemberNum++;
		else	// busy
			readyMemberNum=-1;	// sign to quit scheduling	
	}

	/**
	 * match common schedule with user personal schedule to find new common schedule
	 */
	public void matchSchedule(){
		busy=true;	// set status to busy;
		String fileName=null,currentLine=null, commonSchedule="";
		String initiator=null, nextMember=null, remainMembers=null;
		String [] memberList=null;
		ArrayList<String> newSchedule=new ArrayList<String>();
		Client client = new Client();
		try {
			// read sender file
			fstream = new FileInputStream (fileName=inFile);
			in = new DataInputStream(fstream);
			br = new BufferedReader(new InputStreamReader(in));
			// get initiator info
			currentLine = br.readLine();
			initiator = currentLine;
			// get the list of the remain members
			if((currentLine = br.readLine()).compareTo("")!=0)	// has more members
				if(currentLine.contains(",")){	// have more than one member
					memberList= currentLine.split(",", 2);	// first part: next member, second part: remaining members
					nextMember = memberList[0];	// get next member
					remainMembers = memberList[1]; // get remain member list
					currentLine=br.readLine();	// skip empty line	
				}
				else // only one member left
					nextMember = currentLine;


			// get available schedule
			while((currentLine = br.readLine())!=null){
				if(currentLine.compareTo("\n")!=0&&currentLine.compareTo("")!=0){	// omit empty line
					commonSchedule =commonSchedule.concat(currentLine+",");
				}
			}	
			System.out.println("old common schedule: "+commonSchedule);
			System.out.println("my schedule:"+ mySchedule);
			// compare user's schedule with common schedule
			for(int i=0; i<mySchedule.size();i++){	// iterate through my schedule
				if (commonSchedule.contains(mySchedule.get(i)))	// if find a match
					newSchedule.add(mySchedule.get(i));	// add it to the new schedule
			}
			if(newSchedule.size()!=0){	// have match
				System.out.println("new common schedule: "+newSchedule);
				if(remainMembers!=null){		// have more remaining member to pass to
					// create the output file to send to the next member
					writeNewOutputFile(initiator,remainMembers,newSchedule);
					client.sendFile(nextMember, outFile);// send file to the next member
					System.out.println("send to next member on the list");
				}
				else{	// all member pass
					// create new output file with no remaining member
					writeNewOutputFile("","",newSchedule);
					// return the common schedule to the initiator
					client.schedulingDone(initiator,outFile);
					System.out.println("done shcheduling");
				}
			}
			else{	// no more common schedule
				System.out.println("no common schedule");
				client.noCommonSchedule(initiator);// response to the user
			}

		} catch (FileNotFoundException e) {
			System.out.println("File not found: "+fileName);
		} catch (IOException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 *  create a txt file to send to the next member
	 * @param initiator	user who create the meeting
	 * @param remainMembers list of remaining members
	 * @param newSchedule the new common schedule
	 */
	private void writeNewOutputFile(String initiator, String remainMembers, ArrayList<String> newSchedule) {
		try {
			fWriter = new FileWriter(outFile);
			out = new PrintWriter(fWriter);
			if(remainMembers!=""||initiator!=""){	// have more member
				// first line: initiator info
				out.write(initiator);
				out.println();

				// second line: remaining member list
				if(remainMembers!=""){
					out.write(remainMembers);
					out.println();
				}
				// spacing line
				out.println();
			}
			// from now on: common schedule
			for(int i=0; i<newSchedule.size();i++){
				out.write(newSchedule.get(i));
				if(i!=newSchedule.size()-1)	// not the last element of the new schedule
					out.println();
			}
			out.close();			
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * rewrite the local host schedule to reflect the change
	 * @param meetingTime the meeting time
	 */
	public void saveNewSelfSchedule(String meetingTime){
		// remove the meeting time from my available meeting schedule
		mySchedule.remove(meetingTime);
		try {
			fWriter = new FileWriter(myScheduleFile);
			out = new PrintWriter(fWriter);
			// save new self available schedule on local file
			for(int i=0; i<mySchedule.size();i++){
				out.write(mySchedule.get(i));
				if(i!=mySchedule.size()-1)	// not the last element of the new schedule
					out.println();
			}
			out.close();			
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Broadcast the meeting time to all members
	 */
	public void bCastSchedule(){
		Client client = new Client();
		String meetingTime="";
		try {
			fstream = new FileInputStream (Constants.FINALSCHEDULE);
			in = new DataInputStream(fstream);
			br = new BufferedReader(new InputStreamReader(in));
			// get the first available schedule time
			meetingTime = br.readLine();
			// rewrite the new available schedule
			saveNewSelfSchedule(meetingTime);
			myGUI.updateSystemMessage("Meeting will be on upcoming "+meetingTime);
			System.out.println("tell all members the planed schedule");
			// read self schedule
			for(int i=0;i<readyMemberList.size();i++)
				client.tellMemberSchedule(readyMemberList.get(i),meetingTime);
		} catch (Exception e) {
			e.printStackTrace();
		}
		busy=false;	// reset lock after finish scheduling
	}

	/**
	 * return the list of my schedule
	 * @return ArrayList of my schedule
	 */
	public ArrayList<String> getMySchedule(){
		return mySchedule;
	}

	/**
	 * get the user's schedule from a local txt file
	 */
	private void getSelfSchedule(){
		String fileName="";
		String currentLine="";
		try {		
			// get self schedule
			fstream = new FileInputStream (fileName=myScheduleFile);
			in = new DataInputStream(fstream);
			br = new BufferedReader(new InputStreamReader(in));
			// read self schedule
			while((currentLine = br.readLine())!=null){
				if(currentLine.compareTo("\n")!=0){	// omit empty line
					mySchedule.add(currentLine);
				}
			}			
		} catch (FileNotFoundException e) {
			System.out.println("File not found: "+fileName);
		} catch (IOException e) {
			e.printStackTrace();
		}

	}
}